import { TreeSelect, TreeSelectProps } from 'antd';
import { FC, useEffect, useMemo, useState } from 'react';

import warning from 'warning';

import { getTempData } from '@/biz-components/_common/wrapXhr';

import { isValueEqual, transformValue } from '@/biz-components/_common/util';

// @ts-ignore
import template from 'es6-template-strings';
import { getPathValue } from '@/biz-components/_common/valueUtil';
import { ApiConfig } from "@/typings";

interface AsyncTreeSelectProps<T = any> extends TreeSelectProps<T> {
  disabledTypes?: string[]; // 禁止的类型
  checkableTypes?: string[]; // 可以选择的 类型
  parentKey?: string; // 父级的 key
  idKey: string;
  valueTemplateKey?: string;
  labelTemplateKey?: string;
  remoteDataPath?: string[];
  api: ApiConfig; // 远程地址
  editable?: boolean; // 是否是编辑模式
  selectableCb?: (node: any) => boolean; // 自定义 禁止选择
  changeKey?: string; // 提交给 onChange 的 key
  extraData?: treeDataItem[]; // 额外参数
  parentNodeDisabled?: boolean; // 是否禁用父级节点
}
const TEMPLATE_REG_EXP = /\${[0-9.A-Za-z]*}/;
interface treeDataItem {
  title?: string;
  value: string;
  pId?: string;
  [key: string]: any;
}

const TransformTreeSelect: FC<AsyncTreeSelectProps> = ({
  disabledTypes,
  parentKey,
  idKey,
  labelTemplateKey,
  valueTemplateKey,
  checkableTypes,
  api,
  editable,
  value,
  selectableCb,
  onChange,
  changeKey,
  extraData,
  parentNodeDisabled,
  remoteDataPath,
  ...restProps
}) => {
  if (!editable) {
    return <span>{value}</span>;
  }

  const [loading, setLoading] = useState(false);

  const { params, url, method } = api || {};

  warning(!(method && url), '请配置 api 下的 url method');

  const [treeData, setTreeData] = useState<treeDataItem[]>([]);

  const transformTreeData = (result: any[]) => {
    const newData = result?.map((item) => {
      const { name, code, type } = item;

      let disabled = false;

      if (disabledTypes?.length) {
        disabled = disabledTypes.includes(type);
      }

      if (checkableTypes?.length) {
        disabled = !checkableTypes.includes(type);
      }

      if (parentNodeDisabled && parentKey) {
        disabled = !!result.find((current) => current[parentKey] === code);
      }

      if (selectableCb) {
        disabled = selectableCb(item);
      }

      const isLabelTemplateString = TEMPLATE_REG_EXP.test(labelTemplateKey || '');
      const isValueTemplateString = TEMPLATE_REG_EXP.test(valueTemplateKey || '');

      let newLabel = labelTemplateKey ? item[labelTemplateKey] : name;
      let newValue = valueTemplateKey ? item[valueTemplateKey] : code;

      if (isLabelTemplateString) {
        newLabel = template(labelTemplateKey, item);
      }

      if (isValueTemplateString) {
        newValue = template(valueTemplateKey, item);
      }

      return {
        id: idKey ? item[idKey] : undefined,
        pId: parentKey ? item[parentKey] : undefined,
        title: newLabel,
        value: newValue,
        disabled,
        selectable: disabled,
        ...item,
      };
    });

    setTreeData(newData);
  };

  const search = () => {
    setLoading(true);
    getTempData({
      url,
      params,
      type: method,
    })
      .then((res: any) => {
        const remoteData = getPathValue<any[], any>(res, remoteDataPath) || [];
        transformTreeData(remoteData);
      })
      .finally(() => {
        setLoading(false);
      });
  };

  const handleChange = (value: any, label: any, extra: any) => {
    const newValue = Array.isArray(value)
      ? value.map((item) => {
          return transformValue(item, changeKey);
        })
      : transformValue(value, changeKey);

    onChange?.(newValue, label, extra);
  };

  const handleFocus = () => {
    if (treeData.length === 0) {
      search();
    }
  };

  const displayValue = useMemo(() => {
    const data = treeData.concat(extraData || []);

    if (Array.isArray(value)) {
      return value.map((val: any) => {
        const record = data.find((item) =>
          isValueEqual(transformValue(item, changeKey), transformValue(val, changeKey)),
        );
        return record || val;
      });
    }
    return data.find((item) =>
      isValueEqual(transformValue(item, changeKey), transformValue(value, changeKey)),
    );
  }, [extraData, value, treeData]);

  useEffect(() => {
    setTreeData([]);
  }, [JSON.stringify(api)]);

  useEffect(() => {
    if (!treeData.length && value) {
      search();
    }
  }, [treeData, value]);

  return editable ? (
    <TreeSelect
      allowClear
      treeDataSimpleMode
      value={displayValue}
      {...restProps}
      showSearch
      onChange={handleChange}
      style={{
        width: '100%',
      }}
      loading={loading}
      treeData={treeData}
      dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
      onFocus={handleFocus}
      treeNodeFilterProp="title"
    />
  ) : (
    <span>{value}</span>
  );
};

TransformTreeSelect.defaultProps = {
  disabledTypes: [],
  editable: true,
  placeholder: '',
  idKey: 'code',
  parentNodeDisabled: false,
};

export default TransformTreeSelect;
